package controllers

import java.nio.file.Files
import config.{Global, MyAwait, MyFile, MyRow}
import dao.{BlockDao, BlockRangeDao, GenomeDao}

import javax.inject.Inject
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}
import tools.Circos
import utils.{ExecCommand, TableUtils}
import utils.TableUtils.pageForm

import scala.concurrent.ExecutionContext
import scala.io.Source

class GenomeController @Inject()(genomeDao: GenomeDao,
                                 blockDao: BlockDao,
                                 blockRangeDao: BlockRangeDao,
                                 cc: ControllerComponents)
                                (implicit exec: ExecutionContext) extends AbstractController(cc) with MyAwait with MyRow with MyFile {

  def genomePage(species: String) = Action { implicit request =>
    Ok(views.html.browse.genomePage(species))
  }

  def getGenomeData(species: String) = Action { implicit request =>
    val page = pageForm.bindFromRequest.get
    val data = TableUtils.genome(species)
    val orderX = TableUtils.dealMapDataByPage(data, page)
    val total = orderX.size
    val tmpX = orderX.slice(page.offset, page.offset + page.limit)
    Ok(Json.obj("rows" -> tmpX, "total" -> total))
  }

  def genePage(id: Int) = Action.async { implicit request =>
    genomeDao.getById(id).map { x =>
      Ok(views.html.browse.genePage(x))
    }
  }

  def genePageByGeneId(geneid: String) = Action.async { implicit request =>
    genomeDao.getById(TableUtils.geneIdToId(geneid)).map { x =>
      Ok(views.html.browse.genePage(x))
    }
  }

  def getBlockById(id: Int) = Action { implicit request =>
    val blocks = blockDao.getById(id).toAwait
    val exist = blocks.nonEmpty.toString
    if (blocks.nonEmpty) {
      val gene = genomeDao.getById(id).toAwait
      val genes = genomeDao.getByGeneIds(blocks.head.block.split(",")).toAwait



      val circos = Circos.circosPlotAll3(gene, genes)

      val json = genes.map(_.getJsonByT())
      Ok(Json.obj("exist" -> exist, "block" -> json, "circos" -> circos))
    } else {
      Ok(Json.obj("exist" -> exist))
    }
  }

/*
  def getBlockById(id: Int) = Action { implicit request =>
    val blocks = blockDao.getById(id).toAwait
    val exist = blocks.nonEmpty.toString
    if (blocks.nonEmpty) {
      val gene = genomeDao.getById(id).toAwait
      val genes = genomeDao.getByGeneIds(blocks.head.block.split(",")).toAwait

      val gen = (gene +: genes).filter(_.species == "XJDY")

      val (xjdy, chr) = if (gen.exists(_.chr.endsWith(".1"))) {
        (gen.filter(_.chr.endsWith(".1")), "_1")
      } else {
        if (gen.exists(_.chr.endsWith(".2"))) {
          (gen.filter(_.chr.endsWith(".2")), "_2")
        } else {
          if (gen.exists(_.chr.endsWith(".3"))) {
            (gen.filter(_.chr.endsWith(".3")), "_3")
          } else {
            (gen.filter(_.chr.endsWith(".4")), "_4")
          }
        }
      }


      val plotGenes = genes.filter(_.species != "XJDY") ++ xjdy.filter(_ != gene)
      /*      val circosPlot = if (gene.species == "XJDY") {
              gene.chr.endsWith(".1").toString
            } else {
              "true"
            }*/

      val circos = Circos.circosPlotAll3(gene, plotGenes, chr)

      val json = genes.map(_.getJsonByT())
      Ok(Json.obj("exist" -> exist, "block" -> json, "circos" -> circos))
    } else {
      Ok(Json.obj("exist" -> exist))
    }
  }
*/

  def getSeqs(geneid: String, species: String) = Action { implicit request =>
    val cds = s"${Global.samtools} faidx ${Global.path}/fasta/cds/$species.cds $geneid"
    val pep = s"${Global.samtools} faidx ${Global.path}/fasta/pep/$species.pep $geneid"
    val tmpDir = Files.createTempDirectory("tmpDir").toString
    val exec = new ExecCommand
    exec.exect(Array(cds, pep), tmpDir)
    Ok(
      if (exec.isSuccess) {
        val seq = exec.getOutStr.split(">")
        val cdsSeq = seq(1).split("\n").tail.mkString
        val pepSeq = seq.last.split("\n").tail.mkString
        tmpDir.delete
        Json.obj("cds" -> cdsSeq, "pep" -> pepSeq)
      } else {
        tmpDir.delete
        Json.obj("valid" -> "false", "msg" -> exec.getErrStr)
      })
  }

  def getVarInfo(geneid: String) = Action { implicit request =>
    val path = if (Global.isWindow) {
      "H:\\紫花苜蓿数据库\\2022_12_19\\bianyi\\gene"
    } else {
      s"${Global.path}/config/variable"
    }

    val file = s"$path/$geneid.txt".toFile
    if (!file.exists()) {
      Ok(Json.obj("valid" -> "false"))
    } else {
      val lines = file.readLines
      val snp = lines.filter(_.startsWith("SNP")).map(_.split("\t").tail.zipWithIndex.map { y => y._2.toString -> y._1 }.toMap)
      val indel = lines.filter(_.startsWith("Indel")).map(_.split("\t").tail.zipWithIndex.map { y => y._2.toString -> y._1 }.toMap)
      val sv = lines.filter(_.startsWith("SV")).map(_.split("\t").tail.zipWithIndex.map { y => y._2.toString -> y._1 }.toMap)
      Ok(Json.obj("snpData" -> snp, "indelData" -> indel, "svData" -> sv))
    }

  }

  def getNetwork(geneid: String) = Action { implicit request =>
    val path = if (Global.isWindow) {
      "H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape\\network2"
    } else {
      s"${Global.path}/config/network"
    }

    val file = s"$path/$geneid.txt".toFile
    if (!file.exists()) {
      Ok(Json.obj("valid" -> "false"))
    } else {

      val cor = file.readLines.map(_.split("\t"))

      val edges = cor.map { z =>
        val value = "Value:" + z.last
        Json.obj("from" -> z.head, "title" -> value, "to" -> z(1))
      }

      val n = Json.obj("id" -> geneid, "label" -> geneid, "group" -> "geneGroup", "title" -> geneid)
      val nodes = (cor.map(_.head) ++ cor.map(_(1))).distinct.filter(_ != geneid).map { z =>
        Json.obj("id" -> z, "title" -> z)
      }.distinct
      Ok(Json.obj("edges" -> edges, "nodes" -> (n +: nodes), "net" -> cor.map(z => z.zipWithIndex.map(y => y._2.toString -> y._1).toMap)))

    }

  }

  case class FlankData(num: Int, species: String, chr: String, start: Int, end: Int)

  val FlankForm = Form(
    mapping(
      "num" -> number,
      "species" -> text,
      "chr" -> text,
      "start" -> number,
      "end" -> number
    )(FlankData.apply)(FlankData.unapply)
  )


  def getFlank = Action { implicit request =>
    try {

      val data = FlankForm.bindFromRequest.get
      val species = data.species
      val num = data.num
      val cmd = s"${Global.samtools} faidx ${Global.path}/fasta/genome/$species.genome ${data.chr}:${data.start - num}-${data.end + num}"
      val gene = s"${Global.samtools} faidx ${Global.path}/fasta/genome/$species.genome ${data.chr}:${data.start}-${data.end}"
      val exec = new ExecCommand
      val tmpDir = Files.createTempDirectory("tmpDir").toString
      exec.exect(Array(cmd, gene), tmpDir)
      Ok(
        if (exec.isSuccess) {
          val seq = exec.getOutStr.split(">")
          val selectSeq = seq(1).split("\n").tail.mkString
          val geneSeq = seq.last.split("\n").tail.mkString

          Json.obj("flank" -> selectSeq.replace(geneSeq, "<span class='flank-gene'>" + geneSeq + "</span>"))
        } else {
          println(exec.getErrStr)
          Json.obj("valid" -> "false", "msg" -> "Please enter a positive integer!")
        }
      )
    } catch {
      case e: Exception => Ok(Json.obj("valid" -> "false", "msg" -> "Please enter a positive integer!"))
    }
  }

  def getSnpFreq(geneid: String) = Action { implicit request =>
    val path = if (Global.isWindow) {
      "H:\\紫花苜蓿数据库\\2023_1_9/变异"
    } else {
      s"${Global.path}/config/snp_freq"
    }


    val geneFile = s"$path/gene/${geneid}.txt".toFile

    if (geneFile.exists) {

      val result = geneFile.readLines.map(_.split("\t"))
      val header = result.flatMap { x =>
        x.drop(4).map(_.split(":").head)
      }.distinct

      val freqMap = result.map { x =>
        val pos = x(1)
        val freq = x.drop(4).map(_.split(":")).map(y => y.head -> y.last).toMap
        Map("name" -> pos) ++ freq
      }

      val categories = result.map(_(1))

      val series = header.map { x =>
        Json.obj("name" -> x, "data" -> freqMap.map(y => y.getOrElse(x, "0").toDouble))
      }

      Ok(Json.obj("categories" -> categories, "series" -> series))

    } else {
      Ok(Json.obj("valid" -> "false", "msg" -> "No SNP information in the interval!"))
    }

  }


}
